package com.micro.ai.commons.aspect;

import com.micro.ai.commons.util.JsonUtils;
import com.micro.ai.commons.util.RequestUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import jakarta.servlet.http.HttpServletRequest;

/**
 * 日志切面
 * 
 * @author micro-ai
 * @since 0.0.1
 */
@Slf4j
@Aspect
@Component
public class LogAspect {

    /**
     * 定义切点：所有Controller方法
     */
    @Pointcut("execution(* com.micro.ai..controller..*(..))")
    public void controllerPointcut() {
    }

    /**
     * 定义切点：所有Service方法
     */
    @Pointcut("execution(* com.micro.ai..service..*(..))")
    public void servicePointcut() {
    }

    /**
     * 环绕通知：记录Controller方法调用日志
     */
    @Around("controllerPointcut()")
    public Object logController(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        
        // 获取请求信息
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes != null ? attributes.getRequest() : null;
        
        String className = joinPoint.getTarget().getClass().getSimpleName();
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        
        // 设置MDC上下文
        String requestId = RequestUtils.getRequestId();
        if (requestId != null) {
            MDC.put("requestId", requestId);
        }
        
        Long userId = RequestUtils.getUserId();
        if (userId != null) {
            MDC.put("userId", userId.toString());
        }
        
        String tenantId = RequestUtils.getTenantId();
        if (tenantId != null) {
            MDC.put("tenantId", tenantId);
        }
        
        // 记录请求日志
        if (request != null) {
            log.info("请求开始 - URL: {}, Method: {}, Class: {}, Method: {}, Args: {}", 
                request.getRequestURL(), request.getMethod(), className, methodName, 
                args.length > 0 ? JsonUtils.toJson(args) : "[]");
        } else {
            log.info("请求开始 - Class: {}, Method: {}, Args: {}", 
                className, methodName, args.length > 0 ? JsonUtils.toJson(args) : "[]");
        }
        
        try {
            // 执行方法
            Object result = joinPoint.proceed();
            
            // 记录响应日志
            long endTime = System.currentTimeMillis();
            log.info("请求结束 - Class: {}, Method: {}, 耗时: {}ms, Result: {}", 
                className, methodName, (endTime - startTime), 
                result != null ? JsonUtils.toJson(result) : "null");
            
            return result;
        } catch (Exception e) {
            // 记录异常日志
            long endTime = System.currentTimeMillis();
            log.error("请求异常 - Class: {}, Method: {}, 耗时: {}ms, Error: {}", 
                className, methodName, (endTime - startTime), e.getMessage(), e);
            throw e;
        } finally {
            // 清理MDC上下文
            MDC.clear();
        }
    }

    /**
     * 环绕通知：记录Service方法调用日志
     */
    @Around("servicePointcut()")
    public Object logService(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        
        String className = joinPoint.getTarget().getClass().getSimpleName();
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        
        // 设置MDC上下文
        String requestId = MDC.get("requestId");
        if (requestId != null) {
            MDC.put("service", className + "." + methodName);
        }
        
        // 记录方法调用日志
        log.debug("方法调用开始 - Class: {}, Method: {}, Args: {}", 
            className, methodName, args.length > 0 ? JsonUtils.toJson(args) : "[]");
        
        try {
            // 执行方法
            Object result = joinPoint.proceed();
            
            // 记录方法结束日志
            long endTime = System.currentTimeMillis();
            log.debug("方法调用结束 - Class: {}, Method: {}, 耗时: {}ms", 
                className, methodName, (endTime - startTime));
            
            return result;
        } catch (Exception e) {
            // 记录异常日志
            long endTime = System.currentTimeMillis();
            log.error("方法调用异常 - Class: {}, Method: {}, 耗时: {}ms, Error: {}", 
                className, methodName, (endTime - startTime), e.getMessage(), e);
            throw e;
        }
    }
}
